'use client';

import L, { LatLngExpression, LatLngTuple } from 'leaflet';
import { WsStatus } from '@/app/earth/Map';
import { WebSocketService } from './WebSocketService';
import 'leaflet.heat';
import {CoordTransform} from './CoordTransform';
import { DrawInteraction } from '../drawing/DrawInteraction';
import { SelectInteraction } from '../drawing/SelectInteraction';
import { flattenLatLngs, collectAllPolygons } from '../drawing/drawingUtils';

declare module 'leaflet' {
  namespace L {
    function heatLayer(latlngs: [number, number, number][], options?: any): HeatLayer;
    class HeatLayer extends Layer {
      setOptions(options: any): HeatLayer;
    }
  }
}

export type DrawMode = 'marker' | 'polyline' | 'polygon';
export type SelectMode = 'single' | 'rectangle' | 'polygon';

export class MapService {
  private map: L.Map | null = null;
  private markers: L.CircleMarker[] = [];
  private lines: L.Polyline[] = [];
  private polygons: L.Polygon[] = [];
  private heatLayer: L.HeatLayer | null = null;
  private wsService: WebSocketService;
  private drawInteraction?: DrawInteraction;
  private selectInteraction?: SelectInteraction;
  private _lastSelectMode: SelectMode = 'single';
  
  public destroy() {
    if (this.map) {
      this.map.remove();
      this.map = null;
    }
    if (this.heatLayer) {
      this.heatLayer.remove();
      this.heatLayer = null;
    }
    this.wsService.destroy();
  }
  private markersLayer = L.layerGroup();
  private linesLayer = L.layerGroup();
  private polygonsLayer = L.layerGroup();
  private layersControl: L.Control.Layers | null = null; // 图层控制器
  private _tempLayer?: L.Layer;
  private _tempMarkers: L.CircleMarker[] = [];

  constructor(
    private mapContainer: HTMLElement,
    private setStatus: (status: WsStatus) => void,
    private addLog: (message: any, type: 'client' | 'server') => void
  ) {
    this.wsService = new WebSocketService(
      setStatus,
      addLog,
      this.handleCommand.bind(this)
    );
  }

  private handleCommand(response: any) {
    if (!response?.command) return;

    const { command, params } = response;
    switch (command) {
      case 'init':
        if (params?.center && params?.zoom) {
          this.init(params.center, params.zoom, true); // 强制更新地图视图
        } else {
          this.addLog({ type: 'error', message: 'Invalid params in init' }, 'server');
        }
        break;

      case 'clearLayers':
        this.clearLayers(params?.layerType || 'all', false);
        break;
      case 'zoomMap':
        if (params?.center && typeof params?.zoom === 'number') {
          this.zoomMap(params.center, params.zoom);
        } else {
          this.addLog({ type: 'error', message: 'Invalid params in zoomMap' }, 'server');
        }
        break;
      case 'addMarker':
        if (Array.isArray(params?.coordinate) && params.coordinate.length === 2) {
          this.addMarker(params.coordinate[0], params.coordinate[1], params?.color, params?.size, false);
        } else {
          this.addLog({ type: 'error', message: 'Invalid coordinate format in addMarker' }, 'server');
        }
        break;
      case 'drawLine':
        this.drawLine(params.coordinates, params?.color, params?.weight, false);
        break;
      case 'drawPolygon':
        if (Array.isArray(params?.coordinates) && params.coordinates.length > 0) {
          const coordinates = params.coordinates.map((c: LatLngTuple) => [c[0], c[1]]);
          this.drawPolygon(coordinates, params?.color, params?.fillColor, params?.weight, false);
        } else {
          this.addLog({ type: 'error', message: 'Invalid coordinates format in drawPolygon' }, 'server');
        }
        break;
      case 'drawBoundary':
        if (params?.province && params?.provinceGeoJSON) {
          this.drawBoundary(params.province, params.provinceGeoJSON, false);
        } else {
          this.addLog({ type: 'error', message: '缺少 province 或 provinceGeoJSON 参数' }, 'server');
        }
        break;
      case 'generateHeatmap':
        if (params?.heatPoints) {
          this.renderHeatmapFromServer(params.heatPoints, params.heatmapOptions);
        } else {
          this.addLog({ type: 'error', message: '缺少 heatPoints 或 geoms 参数' }, 'server');
        }
        break;
      case 'clearHeatmap':
        this.clearHeatmap();
        break;
      case 'coordinateTransform':
        if (
          Array.isArray(params?.coordinates) &&
          typeof params?.from === 'string' &&
          typeof params?.to === 'string'
        ) {
          this.handleCoordinateTransform(params.coordinates, params.from, params.to);
        } else {
          this.addLog({ type: 'error', message: 'Invalid parameters in coordinateTransform' }, 'server');
        }
        break;
      default:
        this.addLog({ type: 'error', message: `未知命令: ${command}` }, 'server');
    }
  }

  init(center: [number, number] = [39.9042, 116.4074], zoom = 13, forceUpdate = false) {
    // 如果地图已经存在并且不强制更新，直接返回
    if (this.map && !forceUpdate) return;
  
    // 如果已有地图实例，清除已有图层和控制器
    if (this.map) {
      this.map.eachLayer((layer) => {
        this.map?.removeLayer(layer);  // 移除所有图层
      });
      // 移除控制器
      if (this.layersControl) {
        this.map.removeControl(this.layersControl);  // 移除控制器
      }
    } else {
      // 初始化地图对象
      this.map = L.map(this.mapContainer);
    }
  
    // 确保this.map不为null后再调用方法
    if (this.map) {
      // 设置地图视图
      this.map.setView(center, zoom);
  
      // 添加瓦片图层
      L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        maxZoom: 19,
        attribution: '© OpenStreetMap contributors'
      }).addTo(this.map);
  
      // 清空之前的图层内容
      this.markersLayer.clearLayers(); // 清空标记图层
      this.linesLayer.clearLayers();   // 清空线条图层
      this.polygonsLayer.clearLayers(); // 清空多边形图层
  
      // 添加图层
      this.markersLayer.addTo(this.map);
      this.linesLayer.addTo(this.map);
      this.polygonsLayer.addTo(this.map);
  
      // 创建图层控制器
      this.layersControl = L.control.layers({}, {
        "标记": this.markersLayer,
        "线条": this.linesLayer,
        "多边形": this.polygonsLayer
      }, { collapsed: false }).addTo(this.map);
    }
  }

  clearLayers(layerType: string = 'all', notify = true) {
    if (layerType ==='marker' || layerType === 'markers' || layerType === 'all') {
      this.markers.forEach(m => m.remove());
      this.markers = [];
      this.markersLayer.clearLayers();
    }
    if (layerType === 'line' || layerType === 'lines' || layerType === 'all') {
      this.lines.forEach(l => l.remove());
      this.lines = [];
      this.linesLayer.clearLayers();
    }
    if (layerType === 'polygon' || layerType === 'polygons' || layerType === 'all') {
      this.polygons.forEach(polygon => {
        if (polygon && polygon.remove) {
          polygon.remove();
        }
      });
      this.polygons = [];
      if (this.polygonsLayer) {
        this.polygonsLayer.clearLayers();
        this.polygonsLayer.eachLayer(layer => {
          if (layer && layer.remove) {
            layer.remove();
          }
        });
      }
    }
    if (notify) this.wsService.sendCommand('clearLayers', { layerType });
  }

  addMarker(lat: number, lng: number, color = '#3388ff', size = 5, notify = true) {
    const marker = L.circleMarker([lat, lng], {
      radius: size,
      fillColor: color,
      color: color,
      weight: 1,
      fillOpacity: 0.8
    }).addTo(this.markersLayer);
    
    this.markers.push(marker);
    this.map?.flyTo([lat, lng], this.map.getZoom());
    if (notify) this.wsService.sendCommand('addMarker', { lat, lng, color, size });
  }

  drawLine(coordinates: LatLngExpression[], color = '#3388ff', weight = 3, shouldNotify = true) {
    const line = L.polyline(coordinates, { 
      color, 
      weight, 
      opacity: 1,
      lineJoin: 'round'
    }).addTo(this.linesLayer);
    this.lines.push(line);
    if (shouldNotify) this.wsService.sendCommand('drawLine', { coordinates, color, weight });
  }

  drawPolygon(coordinates: LatLngExpression[], color = '#3388ff', fillColor = '#3388ff', weight = 3, shouldNotify = true) {
    const polygon = L.polygon(coordinates, {
      color,
      fillColor,
      weight,
      opacity: 1,
      fillOpacity: 0.5,
      lineJoin: 'round'
    }).addTo(this.polygonsLayer);
    this.polygons.push(polygon);
    if (shouldNotify) this.wsService.sendCommand('drawPolygon', { coordinates, color, fillColor, weight });
  }

  clearHeatmap() {
    if (this.heatLayer && this.map) {
      this.map.removeLayer(this.heatLayer);
      this.heatLayer = null;
    }
  }

  renderHeatmapFromServer(heatPoints: [number, number, number][], heatmapOptions: any = {}) {
    if (!this.map) return;
  
    this.clearHeatmap();
  
    this.heatLayer = L.heatLayer(heatPoints, {
      radius: heatmapOptions.radius ?? 15,
      blur: heatmapOptions.blur ?? 10,
      maxZoom: heatmapOptions.maxZoom ?? 16,
      minOpacity: heatmapOptions.minOpacity ?? 0.3,
      gradient: heatmapOptions.gradient ?? {
        0.1: 'blue',
        0.3: 'lime',
        0.7: 'orange',
        1.0: 'red'
      }
    }).addTo(this.polygonsLayer);
  }
  
  drawBoundary(province: string, provinceGeoJSON: any, notify = true) {
    try {
      if (!this.map) return;

      // 递归清除 polygonsLayer 上所有 geojson 高亮
      this.polygonsLayer.eachLayer((layer: any) => {
        if (layer && layer.setStyle && layer._originalStyle) {
          layer.setStyle(layer._originalStyle);
        }
        if (layer && layer.eachLayer) {
          layer.eachLayer((subLayer: any) => {
            if (subLayer && subLayer.setStyle && subLayer._originalStyle) {
              subLayer.setStyle(subLayer._originalStyle);
            }
          });
        }
      });

      if (typeof provinceGeoJSON === 'string') {
        provinceGeoJSON = JSON.parse(provinceGeoJSON);
      }

      const boundary = L.geoJSON(provinceGeoJSON, {
        style: {
          color: "#3388ff",
          weight: 3,
          opacity: 1,
          fillColor: "#3388ff",
          fillOpacity: 0.2
        }
      }).addTo(this.polygonsLayer);

      // 新增：递归收集所有实际 polygon
      const polygons = collectAllPolygons(boundary);
      this.polygons.push(...polygons);

      this.map.fitBounds(boundary.getBounds());

      if (notify) {
        this.wsService.sendCommand('drawBoundary', { province, provinceGeoJSON });
      }
    } catch (error) {
      console.error("绘制边界失败:", error);
    }
  }

  handleCoordinateTransform(coordinates: [number, number][], from: string, to: string) {
    const convertFn = this.getTransformFunction(from, to);
    if (!convertFn) return;

    const converted = coordinates.map(([lng, lat]) => convertFn(lng, lat));
    converted.forEach(([lng, lat]) => {
      this.addMarker(lat, lng, '#ff9900', 5, false);
    });
  
    this.map?.flyTo([converted[0][1], converted[0][0]], this.map.getZoom());
  }
  
  getTransformFunction(from: string, to: string) {
    const key = `${from}->${to}`;
    const map: Record<string, (lng: number, lat: number) => [number, number]> = {
      "WGS84->GCJ02": CoordTransform.wgs84ToGcj02,
      "GCJ02->WGS84": CoordTransform.gcj02ToWgs84,
      "WGS84->BD09": CoordTransform.wgs84ToBd09,
      "BD09->WGS84": CoordTransform.bd09ToWgs84,
      "GCJ02->BD09": (lng, lat) => CoordTransform.wgs84ToBd09(...CoordTransform.gcj02ToWgs84(lng, lat)),
      "BD09->GCJ02": (lng, lat) => CoordTransform.wgs84ToGcj02(...CoordTransform.bd09ToWgs84(lng, lat))
    };
    return map[key] || null;
  }

  /**
   * 缩放地图到指定中心和级别
   */
  public zoomMap(center: [number, number], zoom: number, notify = false) {
    if (!this.map) return;
    this.map.setView(center, zoom);
    if (notify) {
      this.wsService.sendCommand('zoomMap', { center, zoom });
    }
  }

  public getDrawInteraction() {
    if (!this.drawInteraction && this.map) {
      console.log('[DEBUG] getDrawInteraction: initializing new DrawInteraction');
      this.drawInteraction = new DrawInteraction(this.map);
    }
    console.log('[DEBUG] getDrawInteraction:', this.drawInteraction);
    return this.drawInteraction;
  }
  public getSelectInteraction() {
    if (!this.selectInteraction && this.map) {
      this.selectInteraction = new SelectInteraction(this.map);
    }
    return this.selectInteraction;
  }


  public getMarkers() { return this.markers; }
  public getLines() { return this.lines; }
  public getPolygons() { return this.polygons; }
  public getLastSelectMode() { return this._lastSelectMode; }

  public getMap() {
    return this.map;
  }

  // --- 以下方法已迁移至 drawing/mapDrawingActions.ts ---
  // import {
  //   startDrawMode,
  //   stopDrawMode,
  //   enableSelectMode,
  //   disableSelectMode,
  //   getSelectedLayer,
  //   fitBounds
  // } from '../drawing/mapDrawingActions';
  // --- END ---
}
